package org.pentaho.redis.output;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.*;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.BaseStepMeta;
import org.pentaho.di.trans.step.StepDialogInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.ui.core.widget.ComboVar;
import org.pentaho.di.ui.core.widget.LabelText;
import org.pentaho.di.ui.trans.step.BaseStepDialog;

import java.util.List;
import java.util.*;

public class RedisStepDialog extends BaseStepDialog implements StepDialogInterface {

    /**
     * The PKG member is used when looking up internationalized strings.
     * The properties file with localized keys is expected to reside in
     * {the package of the class specified}/messages/messages_{locale}.properties
     */
    private static Class<?> PKG = RedisStepMeta.class; // for i18n purposes

    // this is the object the stores the step's settings
    // the dialog reads the settings from it when opening
    // the dialog writes the settings to it when confirmed
    private RedisStepMeta meta;

    private Map<String, Integer> inputFields;
    private String[] fieldNames;
    private boolean gotPreviousFields = false;

    private LabelText wRedisUrl;
    private LabelText wRedisKey;

    private Label wlRedisKeyInField;
    private Button wRedisKeyInField;

    private Label wlRedisKeyField;
    private ComboVar wRedisKeyField;

    private Label wlRedisTypeField;
    private Combo wRedisTypeField;

    private Label wlRedisValueField;
    private ComboVar wRedisValueField;
    private LabelText wMillisecondsToExpire;

    /**
     * @param parent    the SWT shell to open the dialog in
     * @param in        the meta object holding the step's settings
     * @param transMeta transformation description
     * @param sname     the step name
     */
    public RedisStepDialog(Shell parent, Object in, TransMeta transMeta, String sname) {
        super(parent, (BaseStepMeta) in, transMeta, sname);
        meta = (RedisStepMeta) in;
        inputFields = new HashMap<String, Integer>();
    }

    public String open() {
        // store some convenient SWT variables
        Shell parent = getParent();
        Display display = parent.getDisplay();

        // SWT code for preparing the dialog
        shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.MIN);
        props.setLook(shell);
        setShellImage(shell, meta);

        // Save the value of the changed flag on the meta object. If the user cancels
        // the dialog, it will be restored to this saved value.
        // The "changed" variable is inherited from BaseStepDialog
        changed = meta.hasChanged();

        // The ModifyListener used on all controls. It will update the meta object to
        // indicate that changes are being made.
        ModifyListener lsMod = new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                meta.setChanged();
            }
        };

        // ------------------------------------------------------- //
        // SWT code for building the actual settings dialog        //
        // ------------------------------------------------------- //
        FormLayout formLayout = new FormLayout();
        formLayout.marginWidth = Const.FORM_MARGIN;
        formLayout.marginHeight = Const.FORM_MARGIN;
        shell.setLayout(formLayout);
        shell.setText(BaseMessages.getString(PKG, "Redis.Shell.Title"));
        int middle = props.getMiddlePct();
        int margin = Const.MARGIN;

        // Stepname line
        wlStepname = new Label(shell, SWT.RIGHT);
        wlStepname.setText(BaseMessages.getString(PKG, "System.Label.StepName"));
        props.setLook(wlStepname);
        fdlStepname = new FormData();
        fdlStepname.left = new FormAttachment(0, 0);
        fdlStepname.right = new FormAttachment(middle, -margin);
        fdlStepname.top = new FormAttachment(0, margin);
        wlStepname.setLayoutData(fdlStepname);

        wStepname = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
        wStepname.setText(stepname);
        props.setLook(wStepname);
        wStepname.addModifyListener(lsMod);
        fdStepname = new FormData();
        fdStepname.left = new FormAttachment(middle, 0);
        fdStepname.right = new FormAttachment(100, 0);
        fdStepname.top = new FormAttachment(0, margin);
        wStepname.setLayoutData(fdStepname);

        // 设置Redis链接
        wRedisUrl = new LabelText(shell, BaseMessages.getString(PKG, "Redis.RedisUrl.Label"), null);
        props.setLook(wRedisUrl);
        wRedisUrl.addModifyListener(lsMod);
        FormData fdRedisUrlName = new FormData();
        fdRedisUrlName.left = new FormAttachment(0, 0);
        fdRedisUrlName.right = new FormAttachment(100, 0);
        fdRedisUrlName.top = new FormAttachment(wStepname, margin);
        wRedisUrl.setLayoutData(fdRedisUrlName);

        // 设置RedisKey
        wRedisKey = new LabelText(shell, "key", null);
        props.setLook(wRedisKey);
        wRedisKey.addModifyListener(lsMod);
        FormData fdRedisKeyName = new FormData();
        fdRedisKeyName.left = new FormAttachment(0, 0);
        fdRedisKeyName.right = new FormAttachment(100, 0);
        fdRedisKeyName.top = new FormAttachment(wRedisUrl, margin);
        wRedisKey.setLayoutData(fdRedisKeyName);

        // 从上个步骤读取key？
        wlRedisKeyInField = new Label(shell, SWT.RIGHT);
        wlRedisKeyInField.setText("从上个步骤读取key?");
        props.setLook(wlRedisKeyInField);
        FormData fdlRedisKeyInField = new FormData();
        fdlRedisKeyInField.left = new FormAttachment(0, 0);
        fdlRedisKeyInField.right = new FormAttachment(middle, 0);
        fdlRedisKeyInField.top = new FormAttachment(wRedisKey, margin);
        wlRedisKeyInField.setLayoutData(fdlRedisKeyInField);
        wRedisKeyInField = new Button(shell, SWT.CHECK);
        props.setLook(wRedisKeyInField);
        FormData fdRedisKeyInField = new FormData();
        fdRedisKeyInField.left = new FormAttachment(middle, margin);
        fdRedisKeyInField.right = new FormAttachment(100, 0);
        fdRedisKeyInField.top = new FormAttachment(wRedisKey, margin);
        wRedisKeyInField.setLayoutData(fdRedisKeyInField);
        wRedisKeyInField.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                activeRedisKeyField();
            }
        });

        // 设置RedisKey字段
        wlRedisKeyField = new Label(shell, SWT.RIGHT);
        wlRedisKeyField.setText("包含key的字段");
        props.setLook(wlRedisKeyField);
        FormData fdlRedisKeyField = new FormData();
        fdlRedisKeyField.left = new FormAttachment(0, 0);
        fdlRedisKeyField.right = new FormAttachment(middle, 0);
        fdlRedisKeyField.top = new FormAttachment(wlRedisKeyInField, margin);
        wlRedisKeyField.setLayoutData(fdlRedisKeyField);

        wRedisKeyField = new ComboVar(transMeta, shell, SWT.BORDER | SWT.READ_ONLY);
        wRedisKeyField.setEditable(true);
        props.setLook(wRedisKeyField);
        wRedisKeyField.addModifyListener(lsMod);
        FormData fdRedisKeyField = new FormData();
        fdRedisKeyField.left = new FormAttachment(middle, margin);
        fdRedisKeyField.right = new FormAttachment(100, -margin);
        fdRedisKeyField.top = new FormAttachment(wlRedisKeyInField, margin);
        wRedisKeyField.setLayoutData(fdRedisKeyField);
        wRedisKeyField.addFocusListener(new FocusListener() {
            public void focusLost(org.eclipse.swt.events.FocusEvent e) {
            }

            public void focusGained(org.eclipse.swt.events.FocusEvent e) {
                Cursor busy = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
                shell.setCursor(busy);
                setStreamFields();
                shell.setCursor(null);
                busy.dispose();
            }
        });

        // 设置类型
        wlRedisTypeField = new Label(shell, SWT.RIGHT);
        wlRedisTypeField.setText("Type");
        props.setLook(wlRedisTypeField);
        FormData fdlRedisTypeField = new FormData();
        fdlRedisTypeField.left = new FormAttachment(0, 0);
        fdlRedisTypeField.right = new FormAttachment(middle, 0);
        fdlRedisTypeField.top = new FormAttachment(wRedisKeyField, margin);
        wlRedisTypeField.setLayoutData(fdlRedisTypeField);

        wRedisTypeField = new Combo(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
        wRedisTypeField.setItems(new String[]{"String", "List", "Set"});
        props.setLook(wRedisTypeField);
        wRedisTypeField.addModifyListener(lsMod);
        FormData fdRedisTypeField = new FormData();
        fdRedisTypeField.left = new FormAttachment(middle, margin);
        fdRedisTypeField.right = new FormAttachment(100, -18);
        fdRedisTypeField.top = new FormAttachment(wRedisKeyField, margin);
        wRedisTypeField.setLayoutData(fdRedisTypeField);
        wRedisTypeField.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent modifyEvent) {
                wMillisecondsToExpire.setEnabled(wRedisTypeField.getText().equals("String"));
            }
        });

        // 设置value字段
        wlRedisValueField = new Label(shell, SWT.RIGHT);
        wlRedisValueField.setText("包含value字段");
        props.setLook(wlRedisValueField);
        FormData fdlRedisValueField = new FormData();
        fdlRedisValueField.left = new FormAttachment(0, 0);
        fdlRedisValueField.right = new FormAttachment(middle, 0);
        fdlRedisValueField.top = new FormAttachment(wRedisTypeField, margin);
        wlRedisValueField.setLayoutData(fdlRedisValueField);

        wRedisValueField = new ComboVar(transMeta, shell, SWT.BORDER | SWT.READ_ONLY);
        wRedisValueField.setEditable(true);
        props.setLook(wRedisValueField);
        wRedisValueField.addModifyListener(lsMod);
        FormData fdRedisValueField = new FormData();
        fdRedisValueField.left = new FormAttachment(middle, margin);
        fdRedisValueField.right = new FormAttachment(100, -margin);
        fdRedisValueField.top = new FormAttachment(wRedisTypeField, margin);
        wRedisValueField.setLayoutData(fdRedisValueField);
        wRedisValueField.addFocusListener(new FocusListener() {
            public void focusLost(org.eclipse.swt.events.FocusEvent e) {
            }

            public void focusGained(org.eclipse.swt.events.FocusEvent e) {
                Cursor busy = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
                shell.setCursor(busy);
                setStreamFields();
                shell.setCursor(null);
                busy.dispose();
            }
        });

        // 过期时间
        wMillisecondsToExpire = new LabelText(shell, BaseMessages.getString(PKG, "Redis.MillisecondsToExpire.Label"), null);
        props.setLook(wMillisecondsToExpire);
        wMillisecondsToExpire.addModifyListener(lsMod);
        FormData fdMillisecondsToExpireName = new FormData();
        fdMillisecondsToExpireName.left = new FormAttachment(0, 0);
        fdMillisecondsToExpireName.right = new FormAttachment(100, 0);
        fdMillisecondsToExpireName.top = new FormAttachment(wRedisValueField, margin);
        wMillisecondsToExpire.setLayoutData(fdMillisecondsToExpireName);

        // OK and cancel buttons
        wOK = new Button(shell, SWT.PUSH);
        wOK.setText(BaseMessages.getString(PKG, "System.Button.OK"));
        wCancel = new Button(shell, SWT.PUSH);
        wCancel.setText(BaseMessages.getString(PKG, "System.Button.Cancel"));
        setButtonPositions(new Button[]{wOK, wCancel}, margin, wMillisecondsToExpire);

        // Add listeners for cancel and OK
        lsCancel = new Listener() {
            public void handleEvent(Event e) {
                cancel();
            }
        };
        lsOK = new Listener() {
            public void handleEvent(Event e) {
                ok();
            }
        };
        wCancel.addListener(SWT.Selection, lsCancel);
        wOK.addListener(SWT.Selection, lsOK);

        // Detect X or ALT-F4 or something that kills this window and cancel the dialog properly
        shell.addShellListener(new ShellAdapter() {
            public void shellClosed(ShellEvent e) {
                cancel();
            }
        });

        // Set/Restore the dialog size based on last position on screen
        // The setSize() method is inherited from BaseStepDialog
        setSize();

        // populate the dialog with the values from the meta object
        populateDialog();

        // Search the fields in the background
        final Runnable runnable = new Runnable() {
            public void run() {
                StepMeta stepMeta = transMeta.findStep(stepname);
                if (stepMeta != null) {
                    try {
                        RowMetaInterface row = transMeta.getPrevStepFields(stepMeta);
                        // Remember these fields...
                        for (int i = 0; i < row.size(); i++) {
                            inputFields.put(row.getValueMeta(i).getName(), Integer.valueOf(i));
                        }
                        setComboBoxes();
                    } catch (KettleException e) {
                        logError(BaseMessages.getString(PKG, "System.Dialog.GetFieldsFailed.Message"));
                    }
                }
            }
        };
        new Thread(runnable).start();

        // restore the changed flag to original value, as the modify listeners fire during dialog population
        meta.setChanged(changed);

        // open dialog and enter event loop
        activeRedisKeyField();
        wMillisecondsToExpire.setEnabled(wRedisTypeField.getText().equals("String"));

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }

        // at this point the dialog has closed, so either ok() or cancel() have been executed
        // The "stepname" variable is inherited from BaseStepDialog
        return stepname;
    }

    protected void setComboBoxes() {
        // Something was changed in the row.
        final Map<String, Integer> fields = new HashMap<String, Integer>();

        // Add the currentMeta fields...
        fields.putAll(inputFields);

        Set<String> keySet = fields.keySet();
        List<String> entries = new ArrayList<>(keySet);

        fieldNames = entries.toArray(new String[entries.size()]);
    }

    private void setStreamFields() {
        if (!gotPreviousFields) {
            String f1 = wRedisKeyField.getText();
            wRedisKeyField.removeAll();
            wRedisKeyField.setItems(fieldNames);
            if (f1 != null) {
                wRedisKeyField.setText(f1);
            }

            String f2 = wRedisValueField.getText();
            wRedisValueField.removeAll();
            wRedisValueField.setItems(fieldNames);
            if (f2 != null) {
                wRedisValueField.setText(f2);
            }

            gotPreviousFields = true;
        }
    }

    /**
     * 更改【从上个步骤读取key？】复选框
     */
    private void activeRedisKeyField() {
        wRedisKey.setEnabled(!wRedisKeyInField.getSelection());
        wlRedisKeyField.setEnabled(wRedisKeyInField.getSelection());
        wRedisKeyField.setEnabled(wRedisKeyInField.getSelection());
    }

    private void populateDialog() {
        wStepname.selectAll();
        wRedisUrl.setText(meta.getRedisUrl());
        wRedisKey.setText(meta.getRedisKey());
        wRedisKeyInField.setSelection(meta.getRedisKeyInField());
        wRedisKeyField.setText(meta.getRedisKeyField());
        wRedisTypeField.setText(meta.getRedisType());
        wRedisValueField.setText(meta.getRedisValueField());
        wMillisecondsToExpire.setText(meta.getMillisecondsToExpire().toString());
    }

    private void cancel() {
        // The "stepname" variable will be the return value for the open() method.
        // Setting to null to indicate that dialog was cancelled.
        stepname = null;
        // Restoring original "changed" flag on the met aobject
        meta.setChanged(changed);
        // close the SWT dialog window
        dispose();
    }

    private void ok() {
        stepname = wStepname.getText();
        meta.setRedisUrl(wRedisUrl.getText());
        meta.setRedisKey(wRedisKey.getText());
        meta.setRedisKeyInField(wRedisKeyInField.getSelection());
        meta.setRedisKeyField(wRedisKeyField.getText());
        meta.setRedisType(wRedisTypeField.getText());
        meta.setRedisValueField(wRedisValueField.getText());
        meta.setMillisecondsToExpire(Long.parseLong(wMillisecondsToExpire.getText()));

        // close the SWT dialog window
        dispose();
    }
}
